import java.awt.Point;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;

import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class PNTransition {
	public final String Id;
	public final String[] InputIds;
	public final String[] OutputIds;
	public final Point Position;
	
	public final int Width = 8;
	public final int Height = 30;
	
	public KeyValueList OnFire = null;
	
	private final Element domElem;
	private Element msgElem = null;
	
	public PNTransition(Element xmlDomElement, String[] inputPlaceIds, String[] outputPlaceIds) {
		domElem = xmlDomElement;
		if(!domElem.getNodeName().equals("transition"))
			throw new RuntimeException("PNPlace constructor called, but not a <place> element: " + domElem);
		if(inputPlaceIds == null) InputIds = new String[0];
		else InputIds = inputPlaceIds;
		if(outputPlaceIds == null) OutputIds = new String[0];
		else OutputIds = outputPlaceIds;
		Id = domElem.getAttribute("id");
		Element gfx = null;
		try{
			NodeList ch = domElem.getChildNodes();
			for(int i=0; i<ch.getLength(); i++)
				if(("graphics").equals(ch.item(i).getNodeName())) {
					gfx = (Element)ch.item(i);
				}else if(("Msg").equals(ch.item(i).getNodeName())) 
					msgElem = (Element)ch.item(i);
			if(gfx == null)
				throw new Exception();
			if(msgElem != null)
				OnFire = parseXmlMsg(elementToString(msgElem));
		}catch(Throwable t){
			throw new RuntimeException("Required tag <graphics> not found.");
		}
		Position = PnmlData.getPointsFromGraphics(gfx)[0];
	}

	public boolean isClick(Point pos){
		if(pos.x < Position.x - Width / 2 || pos.x > Position.x + Width / 2) return false;
		if(pos.y < Position.y - Height / 2 || pos.y > Position.y + Height / 2) return false;		
		return true;
	}
	
	@Override
	public String toString() {
		return "PNTransition [Id=" + Id + ", InputIds="
				+ Arrays.toString(InputIds) + ", OutputIds="
				+ Arrays.toString(OutputIds) + ", Position=" + Position + "]";
	}
	
	public void updateOnFire(PnmlData toUpdate) {
		if(msgElem == null) {
			if(OnFire == null)
				return;
			String msgToAdd = createXmlMsg();
			PnmlData.appendXmlFragment(toUpdate.getDocBuilder(), this.domElem, msgToAdd);
		}else {
			msgElem.getParentNode().removeChild(msgElem);
			msgElem = null;
			if(OnFire != null) {
				String msgToAdd = createXmlMsg();
				PnmlData.appendXmlFragment(toUpdate.getDocBuilder(), this.domElem, msgToAdd);				
			}
		}
		toUpdate.saveToFile();
	}
	
	private String createXmlMsg() {
		StringBuilder ret = new StringBuilder();
		ret.append("<Msg>\n<Head>");
		ret.append("<MsgID>").append(OnFire.getValue("MsgID")).append("</MsgID>\n");
		if(OnFire.lookupKey("Description") >= 0)
			ret.append("<Description>").append(OnFire.getValue("Description")).append("</Description>");
		ret.append("</Head>\n<Body>");
		for(int i=0; i<OnFire.size(); i++) {
			String k = OnFire.keyAt(i);
			String v = OnFire.getValue(k);
			if(k.equals("MsgID") || k.equals("Description"))
				continue;
			ret.append("<Item><Key>").append(k).append("</Key><Value>").append(v).append("</Value></Item>\n");
		}
		ret.append("</Body>\n</Msg>");
		return ret.toString();
	}

	static KeyValueList parseXmlMsg(String line) {
		KeyValueList ret = new KeyValueList();
		int msgid_s = line.indexOf(">", line.indexOf("<MsgID>")) + 1;
		int msgid_e = line.indexOf("<", msgid_s);
		ret.addPair("MsgID", line.substring(msgid_s, msgid_e).trim());

		int des = line.indexOf("<Description>", msgid_e);
		if (des > 0) {
			int desp_s = line.indexOf(">", des) + 1;
			int desp_e = line.indexOf("<", desp_s);
			ret.addPair("Description", line.substring(desp_s, desp_e).trim());
		}

		int key_s = msgid_e, key_e = msgid_e, value_s = msgid_e, value_e = msgid_e;
		while (true) {
			key_s = line.indexOf("<Key>", value_e);
			if (key_s != -1)
				key_s = line.indexOf(">", key_s) + 1;
			else
				break;
			key_e = line.indexOf("<", key_s);
			String k = line.substring(key_s, key_e).trim();

			value_s = line.indexOf("<Value>", key_e);
			if (value_s != -1)
				value_s = line.indexOf(">", value_s) + 1;
			else
				break;
			value_e = line.indexOf("<", value_s);
			ret.addPair(k, line.substring(value_s, value_e).trim());
		}

		return ret;

	}

	static String elementToString(Node n) {
		String name = n.getNodeName();
		short type = n.getNodeType();
		if (type != 1)
			return n.getTextContent();
		StringBuffer sb = new StringBuffer();
		sb.append('<').append(name);
		NamedNodeMap attrs = n.getAttributes();
		if (attrs != null) {
			for (int i = 0; i < attrs.getLength(); i++) {
				Node attr = attrs.item(i);
				sb.append(' ').append(attr.getNodeName()).append("=\"")
						.append(attr.getNodeValue()).append("\"");
			}
		}

		String textContent = null;
		NodeList children = n.getChildNodes();

		if (children.getLength() == 0) {
			if ((textContent = n.getTextContent()) != null
					&& !"".equals(textContent)) {
				sb.append(textContent).append("</").append(name).append('>');
			} else {
				sb.append("/>");
			}
		} else {
			sb.append('>');
			boolean hasValidChildren = false;
			for (int i = 0; i < children.getLength(); i++) {
				String childToString = elementToString(children.item(i));
				if (!"".equals(childToString)) {
					sb.append(childToString);
					hasValidChildren = true;
				}
			}

			if (!hasValidChildren
					&& ((textContent = n.getTextContent()) != null)) {
				sb.append(textContent);
			}

			sb.append("</").append(name).append('>');
		}

		return sb.toString();
	}
		
}
